/**
 * 
 */
package cn.edu.bjtu.entity.impl;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.servlet.http.HttpServletRequest;

import org.springframework.context.ApplicationEventPublisher;

import cn.edu.bjtu.alex.rewrite.Context;
import cn.edu.bjtu.constant.Constant;
import cn.edu.bjtu.entity.ExecuteContext;
import cn.edu.bjtu.entity.IDocument;
import cn.edu.bjtu.entity.LibSVMHolderContext;
import cn.edu.bjtu.entity.impl.api.Word2VecDocTransContext;
import cn.edu.bjtu.entity.impl.api.Word2VecFileTransContext;
import cn.edu.bjtu.entity.impl.local.LDABuildExecuteContext;
import cn.edu.bjtu.entity.impl.local.LDATransExecuteContext;
import cn.edu.bjtu.entity.impl.local.LibSVMClssifyExecuteContext;
import cn.edu.bjtu.entity.impl.local.LibSVMExecuteContext;
import cn.edu.bjtu.entity.impl.local.TFIDFExecuteContext;
import cn.edu.bjtu.entity.impl.local.TFIDFTransExecuteContext;
import cn.edu.bjtu.entity.impl.local.Word2VecBuildExecuteContext;
import cn.edu.bjtu.settings.ApplicationConfiguration;
import controllers.BaseController;
import pojo.weichatlab.model.Document;
import util.ReflectionUtils;

/**
 * 里面的类都是并列的,接口相似.
 * 根据不同的命令创建不同的环境
 * 不同的环境Builder自己负责处理自己所需要的数据,然后传递给不同的ExecuteContext类
 * LDA待修改.
 * @author alex
 *
 */
public class ExecuteContextManager {
	/**
	 * 这三个变量维护着执行环境
	 */
	private final static TFIDFBuilder tfidfBuilder = new TFIDFBuilder();
	private final static LDABuilder ldaBuilder = new LDABuilder();
	private final static Word2VecBuilder w2vBuilder = new Word2VecBuilder();
	private final static LibSVMBuilder svmBuilder = new LibSVMBuilder();
	
	
	public static ExecuteContext buildContext(HttpServletRequest req){
		boolean nocache = req.getParameter(Constant.COMMAND_NO_CACHE)!=null;
		String type = req.getParameter(Constant.COMMAND_TYPE);
		if(type == null){
			type = (String)req.getAttribute(Constant.COMMAND_TYPE);
		}
		BaseController controller = (BaseController)req.getAttribute(Constant.COMMAND_EXEC_CONTROLLER);
		ApplicationEventPublisher pub = (ApplicationEventPublisher)req.getAttribute(Constant.COMMAND_PUBLISHER);
		switch(type){
		
		
		//TFIDF Related
		case Constant.COMMAND_TYPE_TFIDF:
			return tfidfBuilder.new BuildTFIDFBuilder().trainSetDate().trainSetNoCache(nocache).build();
		case Constant.COMMAND_TYPE_TFIDF_TRANS:
			return tfidfBuilder.new TFIDFTransBuilder(req).paresDocument().build();
			
		case Constant.COMMAND_TYPE_WORD2VEC:
			return w2vBuilder.new TrainWord2VecModelBuilder(req).setController(controller).setDateNow().setPub(pub).build();
		case Constant.COMMAND_TYPE_WORD2VEC_TRANS:
			return w2vBuilder.new Word2VecTransBuilder(req).parseText().build();
		case Constant.COMMAND_TYPE_WORD2VEC_FILE_TRANS:
			return w2vBuilder.new Word2VecFileTransBuilder(req).parseFileName().build();
		
			
			
		//LDA Related
		case Constant.COMMAND_TYPE_LDA:
			return ldaBuilder.new LDATrainBuilder(req).setController(controller).setDateNow().setPub(pub).build();
		case Constant.COMMAND_TYPE_LDA_TRANS:
			return ldaBuilder.new LDATransBuilder(req).setController(controller).setDateNow().setPub(pub).build();
			
			
		//SVM Related	
		case Constant.COMMAND_TYPE_SVM_TRAIN:
			return svmBuilder.new TrainBuilder(req).parseFilename().build();
		case Constant.COMMAND_TYPE_SVM_TEST:
			return svmBuilder.new TestBuilder(req).parseFilename().parseBatchText().parseText().parseInputDir().parseOutputDir().build();
			
			
			
		//Reflect	
		case Constant.COMMAND_TYPE_LDA_SYNC:
		case Constant.COMMAND_TYPE_WORD2VEC_SYNC:
		case Constant.COMMAND_TYPE_REFLECT:
			String clz = req.getParameter(Constant.COMMAND_TYPE_REFLECT_KEY);
			if(clz == null){
				clz = (String) req.getAttribute(Constant.COMMAND_TYPE_REFLECT_KEY);
			}
			return new ReflectBuilder().setClz(clz).build();
			default:
				return null;
		}
	}
	/**
	 * 根据类名反射创建java类,主要是需要处理锁.............
	 * @author Alex
	 *
	 */
	public static class ReflectBuilder{
		private String pkg = "cn.edu.bjtu.entity.impl.api.";
		String clz  = "";
		String suffix = "Context";
		private ReflectBuilder() {}
		public ReflectBuilder setClz(String s){
			this.clz = s;
			return this;
		}
		public ExecuteContext build(){
			Class<?> c = ReflectionUtils.newClass(pkg+clz+suffix, ExecuteContext.class);
			Object lock = null;
			if(clz.toLowerCase().contains("word2vec")){
				lock = w2vBuilder.getMoelLevelRWLock();
			}else if(clz.toLowerCase().contains("lda")){
				//这样也正确.说明私有内部静态类的私有字段在外面也是可以直接访问的
				//lock = ldaBuilder.buildRWLock;
				lock = ldaBuilder.getMoelLevelRWLock();
			}
			return ReflectionUtils.newInstance(c, ExecuteContext.class, new Object[]{lock});
		}
	}
	public static class LDABuilder{
		private ReadWriteLock buildRWLock = new ReentrantReadWriteLock();
		LDABuildExecuteContext cachedEC = null;
		BaseController controller = null;
		ApplicationEventPublisher pub;
		Date date = null;
		boolean nocache = false;
		private LDABuilder() {}
		
		public ReadWriteLock getMoelLevelRWLock(){
			return buildRWLock; 
		}	
		public class LDATrainBuilder{
			BaseController controller = null;
			ApplicationEventPublisher pub;
			Date date = null;
			HttpServletRequest req;
			private LDATrainBuilder(HttpServletRequest req){
				this.req = req;
			}
			public LDATrainBuilder setController(BaseController o){
				this.controller = o;
				return this;
			}
			public LDATrainBuilder setDateNow(){
				date = new Date();
				return this;
			}
			public LDATrainBuilder setPub(ApplicationEventPublisher pub){
				this.pub = pub;
				return this;
			}
			public LDABuildExecuteContext build(){
				try {
					if(cachedEC == null || nocache){
						cachedEC = new LDABuildExecuteContext(pub, this.date, this.controller, null,buildRWLock);
					}
					return cachedEC;
				} catch(Exception e){
					return null;
				}finally{
				}
			}
		}
		public class LDATransBuilder{
			BaseController controller = null;
			ApplicationEventPublisher pub;
			Date date = null;
			HttpServletRequest req;
			private LDATransBuilder(HttpServletRequest req){
				this.req = req;
			}
			public LDATransBuilder setController(BaseController o){
				this.controller = o;
				return this;
			}
			public LDATransBuilder setDateNow(){
				date = new Date();
				return this;
			}
			public LDATransBuilder setPub(ApplicationEventPublisher pub){
				this.pub = pub;
				return this;
			}
			public LDATransExecuteContext build(){
				return new LDATransExecuteContext(buildRWLock);
			}
		}
	}
	
	public static class Word2VecBuilder{
		private Word2VecBuilder() {
		}
		private ReadWriteLock buildRWLock = new ReentrantReadWriteLock();
		private volatile Word2VecBuildExecuteContext cachedEC = null;	
		public ReadWriteLock getMoelLevelRWLock(){
			return buildRWLock; 
		}
		public Word2VecTransBuilder newDocTrans(Map<String,Object> params){
			return new Word2VecTransBuilder(params);
		}
		/**
		 * 这个内部<b>非静态类</b>是使用WORD2VEC对一个文件进行转换
		 * 参数是通过filename获取,然后读取文件.给http接口传送文件
		 * @see Word2VecTransBuilder 是对一篇文档行转换
		 * @author Alex
		 *
		 */
		public class Word2VecFileTransBuilder{
			String filename = null;
			HttpServletRequest req;
			private Word2VecFileTransBuilder(HttpServletRequest req){
				this.req = req;
			}
			
			public Word2VecFileTransBuilder parseFileName(){
				this.filename = req.getParameter("filename");
				return this;
			}
			public Word2VecFileTransContext build(){
				Word2VecFileTransContext wwww = new Word2VecFileTransContext(Word2VecBuilder.this.buildRWLock);
				wwww.setFilename(filename);
				return wwww;
			}
		}	
		/**
		 * 这个内部<b>非静态类</b>是使用WORD2VEC对一个文档进行转换
		 * 这个文档是通过request.getParameter获取.
		 * @see Word2VecFileTransBuilder 是对一个文件进行转换
		 * @author Alex
		 *
		 */
		public class Word2VecTransBuilder{
			String text = null;
			HttpServletRequest req;
			Map<String,Object> params;			
			private Word2VecTransBuilder(HttpServletRequest req){
				this.req = req;
			}
			private Word2VecTransBuilder(Map<String,Object> params){
				this.params = params;
				
			}
			public Word2VecTransBuilder parseText(){
				if(req!=null){
					text = req.getParameter(Constant.COMMAND_TEXT_TRANS);
				}else{
					text = (String) params.get(Constant.COMMAND_TEXT_TRANS);
				}
				return this;
			}
			public Word2VecDocTransContext build(){
				Word2VecDocTransContext wwww = new Word2VecDocTransContext(buildRWLock);
				wwww.setText(this.text);
				return wwww;
			}
		}
		/**
		 * 这个内部<b>非静态类</b>是用来建立word2vec模型,进行HTTP调用访问李尧的程序.
		 * 这个文档是通过request.getParameter获取.
		 * @see Word2VecFileTransBuilder 是对一个文件进行转换
		 * @author Alex
		 *
		 */
		public class TrainWord2VecModelBuilder{
			BaseController controller = null;
			ApplicationEventPublisher pub;
			Date date = null;
			boolean nocache = false;	
			/**
			 * 这个胡构造函数需要一个REQUEST对象,但是并没使用,为了保持接口的统一????
			 * @param req
			 */
			private TrainWord2VecModelBuilder(HttpServletRequest req){
			}
			public TrainWord2VecModelBuilder setController(BaseController o){
				this.controller = o;
				return this;
			}
			public TrainWord2VecModelBuilder setDateNow(){
				date = new Date();
				return this;
			}
			public TrainWord2VecModelBuilder setPub(ApplicationEventPublisher pub){
				this.pub = pub;
				return this;
			}
			public Word2VecBuildExecuteContext build(){
				if(cachedEC == null || nocache){
					cachedEC = new Word2VecBuildExecuteContext(pub, this.date, this.controller, null,buildRWLock);
				}
				return cachedEC;
			}
		}
		
	}
	public static class LibSVMBuilder{
		private ReadWriteLock buildRWLock = new ReentrantReadWriteLock();
		private LibSVMHolderContext ctx = new LibSVMHolderContext();
		private Date d = null;
		private boolean nocache = true;
		private LibSVMBuilder() {}
		public ReadWriteLock getMoelLevelRWLock(){
			return buildRWLock; 
		}
		/**
		 * 这个内部<b>非静态类</b>用来创建Libsvm分类器.
		 * @author Alex
		 *
		 */
		public class TrainBuilder{
			final String inputdir = ApplicationConfiguration.getInstance().getProperty("LIBSVM_DATA_DIR");
			final String outputdir = ApplicationConfiguration.getInstance().getProperty("LIBSVM_DATA_DIR");
			HttpServletRequest req;
			String filename = null;
			public TrainBuilder(HttpServletRequest req) {
				this.req = req;
			}
			public LibSVMExecuteContext build(){
				LibSVMExecuteContext l = new LibSVMExecuteContext(buildRWLock, this.inputdir, this.outputdir, ctx);
				l.setTargetName(filename);
				return l;
			}
			public TrainBuilder parseFilename(){
				this.filename = req.getParameter("filename");
				return this;
			}
			
		}
		/**
		 * 这个内部<b>非静态类</b>用使用libsvm分类..
		 * Holder是共享变量,它代表共享的分类分类模型...
		 * @author Alex
		 *
		 */
		public class TestBuilder{
			static final String FIELD_INPUT_DIR = "inputdir";
			static final String FIELD_OUTPUT_DIR = "outputdir";
			String inputdir;
			String outputdir;
			String filename ;
			List<String> batchText = null;
			String text = null;
			HttpServletRequest req;
			public TestBuilder(HttpServletRequest req) {
				this.req = req;
			}
			public TestBuilder parseInputDir(){
				
				inputdir = req.getParameter(FIELD_INPUT_DIR);
				return this;
			}
			public TestBuilder parseOutputDir(){
				outputdir = req.getParameter(FIELD_OUTPUT_DIR);
				return this;
			}
			public TestBuilder parseFilename(){
				filename = req.getParameter("filename");
				return this;
			}
			public TestBuilder parseBatchText(){
				String s[] = req.getParameterValues("batchText");
				if(s!=null)
					batchText = Arrays.asList(s);
				return this;
			}
			public TestBuilder parseText(){
				String s = req.getParameter("text");
				text = s;
				return this;
			}
			
			public LibSVMClssifyExecuteContext build(){
				LibSVMClssifyExecuteContext context = new LibSVMClssifyExecuteContext(buildRWLock, this.inputdir, this.outputdir, ctx);
				context.setFileToClassify(filename);
				context.setBatchTextToClassify(batchText);
				context.setTextToClassify(text);
				return context;
			}
			public TestBuilder setIn(String in){
				this.inputdir = in;
				return this;
			}
			public TestBuilder setOut(String out){
				this.outputdir = out;
				return this;
			}	
		}
	}
	/**
	 * TFIDF的并发控制是在Context类里面
	 * 它有一个锁叫transLock的ReadWriteReentrantLock;
	 * @author Alex
	 *
	 */
	public static class TFIDFBuilder{
	
		private TFIDFBuilder() {}
		
		private ReadWriteLock lock = new ReentrantReadWriteLock();
		private volatile Context ctx;

		public ReadWriteLock getMoelLevelRWLock(){
			return lock; 
		}	
		
		public Context getContext(){
			return ctx;
		}
		/**
		 * 这个内部<b>非静态类</b>建立TFIDF模型.
		 * @see Word2VecFileTransBuilder 是对一个文件进行转换
		 * @author Alex
		 *
		 */
		public class BuildTFIDFBuilder{
			private Date d = null;
			private boolean nocache = false;
			String in;
			String out;
			public BuildTFIDFBuilder(){}
			private BuildTFIDFBuilder trainNewCtx(){
				in = ApplicationConfiguration.getInstance().getProperty("TF_IDF_DATASET_DIR");
				out = ApplicationConfiguration.getInstance().getProperty("TF_IDF_RESULT_DIR");
				ctx = new Context(in,out);
				return this;
			}
			public BuildTFIDFBuilder trainSetDate(){
				this.d = new Date();
				return this;
			}
			public BuildTFIDFBuilder trainSetNoCache(boolean b){
				this.nocache = b;
				return this;
			}
			public TFIDFExecuteContext build(){
				if(ctx == null || nocache){
					System.out.println("new a new context");
					trainNewCtx();
				}
				return new TFIDFExecuteContext(lock, in,out, ctx);
			}
		}
		public class TFIDFTransBuilder{
			private Date d = null;
			private boolean nocache = true;
			HttpServletRequest req;
			String doc;
			public TFIDFTransBuilder(HttpServletRequest req){
				this.req = req;
			}
			
			public TFIDFTransBuilder paresDocument(){
				doc = req.getParameter(Constant.COMMAND_TEXT_TRANS);
				testAddDoc(new Document("","",doc));
				return this;
			}
			public TFIDFTransExecuteContext build(){
				return new TFIDFTransExecuteContext(lock, ctx.getInputDirString(), ctx.getOutputDirString(), ctx);
			}
			//FIXME::这里线程有问题,多个用户同时转化可能出问题,因为Context.TransformContext.documents里面的List的非线程安全的.	
			private TFIDFTransBuilder testAddDoc(IDocument doc){
				ctx.addTransformDocument(doc);
				return this;
			}
			private TFIDFTransBuilder testAddDocs(List<IDocument> docs){
				ctx.addTransformDocuments(docs);
				return this;
			}
		}
		
	}
	public static Context getTFIDFContext(){
		return tfidfBuilder.getContext();
	}
	/**
	 * @return the tfidfbuilder
	 */
	public static TFIDFBuilder getTfidfbuilder() {
		return tfidfBuilder;
	}
	/**
	 * @return the ldabuilder
	 */
	public static LDABuilder getLdabuilder() {
		return ldaBuilder;
	}
	/**
	 * @return the w2vbuilder
	 */
	public static Word2VecBuilder getW2vbuilder() {
		return w2vBuilder;
	}
	/**
	 * @return the svmbuilder
	 */
	public static LibSVMBuilder getSvmbuilder() {
		return svmBuilder;
	}
	
	
	
	
	
	
}
